package com.configurer;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4;
import com.core.BizException;
import com.core.Result;
import com.core.ResultCode;
import com.util.FastJsonFilter;
import com.util.LoginTicketUtil;
import com.util.SystemConstant;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Spring MVC 配置
 */
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    private final Log logger = LogFactory.getLog(getClass());

    @Value("${spring.profiles.active}")
    private String env;//当前激活的配置文件

    //使用阿里 FastJson 作为JSON MessageConverter
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter4 converter = new FastJsonHttpMessageConverter4();
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullNumberAsZero,
                SerializerFeature.DisableCircularReferenceDetect);
        converter.setFastJsonConfig(config);
        converter.setDefaultCharset(Charset.forName("UTF-8"));
        converters.add(converter);
    }


    //统一异常处理
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        exceptionResolvers.add(new HandlerExceptionResolver() {
            public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
                boolean isApi = false;
                String uri = request.getRequestURI();
                if (uri.indexOf("/api/") != -1||uri.indexOf("/gateway") != -1) {
                    isApi = true;
                }
                if (!isApi) {
                    String header = request.getHeader("X-Requested-With");
                    if (!"XMLHttpRequest".equals(header)) {//非ajax请求
                        ModelAndView mav = new ModelAndView();
                        mav.setViewName("error");
                        return mav;
                    }
                }
                Result result = new Result();
                if (e instanceof BizException) {//业务失败的异常，如“账号或密码错误”
                    Integer code=((BizException) e).getCode();
                    if(code==null)
                    {
                        result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
                    }
                    else {
                        result.setCode(code).setMessage(e.getMessage());
                    }
                    logger.info(e.getMessage());
                } else if (e instanceof NoHandlerFoundException) {
                    result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + request.getRequestURI() + "] 不存在");
                } else if (e instanceof ServletException) {
                    result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
                } else {
                    result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误，请联系管理员");
                    String message;
                    if (handler instanceof HandlerMethod) {
                        HandlerMethod handlerMethod = (HandlerMethod) handler;
                        message = String.format("接口 [%s] 出现异常，方法：%s.%s，异常摘要：%s",
                                request.getRequestURI(),
                                handlerMethod.getBean().getClass().getName(),
                                handlerMethod.getMethod().getName(),
                                e.getMessage());
                    } else {
                        message = e.getMessage();
                    }
                    logger.error(message, e);
                }
                responseResult(response, result,200);
                return null;
            }

        });
    }

    //解决跨域问题
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //registry.addMapping("/**");
    }

    //添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                if (request.getSession().getAttribute(SystemConstant.SESSION_ADMIN) == null) {
                    String header = request.getHeader("X-Requested-With");
                    if ("XMLHttpRequest".equals(header)) {//ajax
                        Result result = new Result();
                        result.setCode(ResultCode.UNLOGIN).setMessage("您当前的会话已超时，请重新登录。");
                        result.setBody("/backend/loginIndex");
                        responseResult(response, result,403);
                        return false;
                    }
                    response.sendRedirect("/backend/loginIndex");
                    return false;

                }
                return true;
            }
        }).addPathPatterns("/backend/security/**");
        registry.addInterceptor(new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                if (request.getSession().getAttribute(SystemConstant.SESSION_HOTEL_ADMIN) == null) {
                    String header = request.getHeader("X-Requested-With");
                    if ("XMLHttpRequest".equals(header)) {//ajax
                        Result result = new Result();
                        result.setCode(ResultCode.UNLOGIN).setMessage("您当前的会话已超时，请重新登录。");
                        result.setBody("/hotel/login");
                        responseResult(response, result,403);
                        return false;
                    }
                    response.sendRedirect("/hotel/loginIndex");
                    return false;

                }
                return true;
            }
        }).addPathPatterns("/hotel/security/**");
        registry.addInterceptor(new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                if (request.getSession().getAttribute(SystemConstant.SESSION_AGENT_ADMIN) == null) {
                    String header = request.getHeader("X-Requested-With");
                    if ("XMLHttpRequest".equals(header)) {//ajax
                        Result result = new Result();
                        result.setCode(ResultCode.UNLOGIN).setMessage("您当前的会话已超时，请重新登录。");
                        result.setBody("/agent/login");
                        responseResult(response, result,403);
                        return false;
                    }
                    response.sendRedirect("/agent/loginIndex");
                    return false;

                }
                return true;
            }
        }).addPathPatterns("/agent/security/**");
        registry.addInterceptor(new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

                boolean pass = true;
                if (!"noNeed".equals(request.getHeader("ValidateSign"))) {
                    // 验证签名
                    pass = true;
                }

                if (pass) {
                    return true;
                } else {
                    logger.warn("签名认证失败，请求接口：" + request.getRequestURI() + "，请求IP：" + getIpAddress(request) + "，请求参数：" + JSON.toJSONString(request.getParameterMap())
                    );

                    Result result = new Result();
                    result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败");
                    responseResult(response, result,200);
                    return false;
                }
            }
        }).addPathPatterns("/api/**");
    }

    /**
     * 一个简单的签名认证，规则：
     * 1. 将请求参数按ascii码排序
     * 2. 拼接为a=value&b=value...这样的字符串（不包含sign）
     * 3. 混合密钥（secret）进行md5后获得签名，与请求的签名进行比较
     */
    private boolean validateSign(HttpServletRequest request) {
        String requestSign = request.getParameter("sign");//获得请求签名，如sign=19e907700db7ad91318424a97c54ed57
        if (StringUtils.isEmpty(requestSign)) {
            return false;
        }
        List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
        keys.remove("sign");//排除sign参数
        Collections.sort(keys);//排序

        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            sb.append(key).append("=").append(request.getParameter(key)).append("&");//拼接字符串
        }
        String linkString = sb.toString();
        linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);//去除最后一个'&'


        String sign = DigestUtils.md5Hex(linkString + SystemConstant.SIGN_SECRET).toUpperCase();//混合密钥md5
        // 获得签名验证结果
        boolean isSign = sign.equals(requestSign);
        if (isSign) {
            String uri = request.getRequestURI();
            if (uri.indexOf("/api/shop/") >= 0) {
                Integer adminId = NumberUtils.toInt(request.getParameter("adminId"), -1);
                if (adminId > 0) {
                    String loginTicket = request.getHeader(SystemConstant.LOGIN_TICKET);
                    return LoginTicketUtil.validateTicket(adminId ,Integer.parseInt(request.getParameter("shopId")), loginTicket);
                }
            }
        }

        return isSign;
    }

    private void responseResult(HttpServletResponse response, Result result,int status) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setStatus(status);
        try {
            response.getWriter().write(FastJsonFilter.ReturnApiResult(result));
            response.getWriter().flush();
            response.getWriter().close();
        } catch (IOException ex) {
            logger.error(ex.getMessage());
        }
    }
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 如果是多级代理，那么取第一个ip为客户端ip
        if (ip != null && ip.indexOf(",") != -1) {
            ip = ip.substring(0, ip.indexOf(",")).trim();
        }

        return ip;
    }
}
